/**
 * Copyright (C) 2015 Paul Cech
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.eazegraph.lib.charts;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;
import org.eazegraph.lib.models.BarModel;
import org.eazegraph.lib.models.BaseModel;
import org.eazegraph.lib.utils.LogUtil;
import org.eazegraph.lib.utils.Utils;

import java.util.ArrayList;
import java.util.List;

/**
 * A simple Bar Chart where the bar heights are dependent on each other.
 */
public class BarChart extends BaseBarChart {
    /**
     * * The constant TAG
     */
    private static final String TAG = "BarChart";
    /**
     * * The constant LOG_TAG
     */
    private static final String LOG_TAG = BarChart.class.getSimpleName();
    /**
     * The constant M data
     */
    private List<BarModel> mData;
    /**
     * The constant M value paint
     */
    private Paint mValuePaint;
    /**
     * The constant M value distance
     */
    private int mValueDistance = (int) Utils.dpToPx(getContext(), 3);

    /**
     * Simple constructor to use when creating a view from code.
     *
     * @param context The Context the view is running in, through which it can                access the current theme, resources, etc.
     */
    public BarChart(Context context) {
        super(context);

        initializeGraph();
    }

    /**
     * Constructor that is called when inflating a view from XML. This is called
     * when a view is being constructed from an XML file, supplying attributes
     * that were specified in the XML file. This version uses a default style of
     * 0, so the only attribute values applied are those in the Context's Theme
     * and the given AttributeSet.
     * <p/>
     * <p/>
     * The method onFinishInflate() will be called after all children have been
     * added.
     *
     * @param context The Context the view is running in, through which it can                access the current theme, resources, etc.
     * @param attrs   The attributes of the XML tag that is inflating the view.
     * @see # #nt)
     */
    public BarChart(Context context, AttrSet attrs) {
        super(context, attrs);
        initializeGraph();
    }

    /**
     * Adds a new {@link BarModel} to the BarChart.
     *
     * @param _Bar The BarModel which will be added to the chart.
     */
    public void addBar(BarModel _Bar) {
        if (_Bar == null) {
            return;
        }        
        mData.add(_Bar);
        onDataChanged();
    }

    /**
     * Adds a new list of {@link BarModel} to the BarChart.
     *
     * @param _List The BarModel list which will be added to the chart.
     */
    public void addBarList(List<BarModel> _List) {
        if (_List == null) {
            return;
        }
        mData = _List;
        onDataChanged();
    }

    /**
     * Returns the data which is currently present in the chart.
     *
     * @return The currently used data.
     */
    @Override
    public List<BarModel> getData() {
        return mData;
    }

    /**
     * Resets and clears the data object.
     */
    @Override
    public void clearChart() {
        mData.clear();
    }

    /**
     * On touch event boolean
     *
     * @param component  component
     * @param touchEvent touch event
     * @return the boolean
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        super.onTouchEvent(component, touchEvent);
        LogUtil.info(TAG, "BarChart onTouchEvent");
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            simulateClick();
            return false;
        } else {
            return false;
        }
    }

    /**
     * This is the main entry point after the graph has been inflated. Used to initialize the graph
     * and its corresponding members.
     */
    @Override
    protected void initializeGraph() {
        super.initializeGraph();
        mData = new ArrayList<>();
        mValuePaint = new Paint(mLegendPaint);
        mValuePaint.setTextAlign(TextAlignment.CENTER);
    }

    /**
     * Should be called after new data is inserted. Will be automatically called, when the view dimensions
     * has changed.
     */
    @Override
    protected void onDataChanged() {
        calculateBarPositions(mData.size());
        super.onDataChanged();
    }

    /**
     * Calculates the bar boundaries based on the bar width and bar margin.
     *
     * @param _Width  Calculated bar width
     * @param _Margin Calculated bar margin
     */
    protected void calculateBounds(float _Width, float _Margin) {
        float maxValue = 0;
        int last = 0;

        for (BarModel model : mData) {
            if (model.getValue() > maxValue) {
                maxValue = model.getValue();
            }
        }

        int valuePadding = mShowValues ? (int) mValuePaint.getTextSize() + mValueDistance : 0;

        float heightMultiplier = (mGraphHeight - valuePadding) / maxValue;

        for (BarModel model : mData) {
            float height = model.getValue() * heightMultiplier;
            last += _Margin / 2;
            model.setBarBounds(new RectFloat(last, mGraphHeight - height, last + _Width, mGraphHeight));
            model.setLegendBounds(new RectFloat(last, 0, last + _Width, mLegendHeight));
            last += _Width + (_Margin / 2);
        }

        Utils.calculateLegendInformation(getContext(), mData, 0, mContentRect.getWidth(), mLegendPaint);
    }

    /**
     * Callback method for drawing the bars in the child classes.
     *
     * @param _Canvas The canvas object of the graph view.
     */
    protected void drawBars(Canvas _Canvas) {

        for (BarModel model : mData) {
            RectFloat bounds = model.getBarBounds();
            mGraphPaint.setColor(new Color(model.getColor()));
            LogUtil.info(TAG, " drawBars: " + bounds.toString());
            _Canvas.drawRect(new RectFloat(bounds.left,
                    bounds.bottom - (bounds.getHeight() * mRevealValue),
                    bounds.right,
                    bounds.bottom), mGraphPaint);

            if (mShowValues) {
                _Canvas.drawText(mValuePaint, Utils.getFloatString(model.getValue(), mShowDecimal), model.getLegendBounds().getCenter().getPointX(),
                        bounds.bottom - (bounds.getHeight() * mRevealValue) - mValueDistance);
            }
        }
    }

    /**
     * Returns the list of data sets which hold the information about the legend boundaries and text.
     *
     * @return List of BaseModel data sets.
     */
    @Override
    protected List<? extends BaseModel> getLegendData() {
        return mData;
    }

    /**
     * Get bar bounds list
     *
     * @return the list
     */
    @Override
    protected List<RectFloat> getBarBounds() {
        ArrayList<RectFloat> bounds = new ArrayList<RectFloat>();
        for (BarModel model : mData) {
            bounds.add(model.getBarBounds());
        }
        return bounds;
    }
}
